package com.datasure.cloudsure.redis.config;

import com.datasure.cloudsure.redis.builder.RedisTemplaterFactoryBuild;
import com.datasure.cloudsure.redis.provider.RedisProvider;
import com.datasure.cloudsure.redis.provider.impl.YamlRedisProvider;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.datasure.cloudsure.redis.config.property.DynamicRedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

@Configuration
@ComponentScan("com.datasure.cloudsure.redis")
public class DynamicRedisConfig {

    /**
     * 提供给外部使用的RedisTemplater工厂类
     * 用于获取动态RedisTemplater以及默认RedisTemplater
     * @param redisConnectionFactory 默认配置连接工厂
     * @param dynamicRedisProperties 动态数据源配置
     * @return
     * @throws Exception
     */
    @Bean
    public RedisTemplaterFactoryBuild redisTemplaterWrapperFactoryBuild(RedisConnectionFactory redisConnectionFactory, DynamicRedisProperties dynamicRedisProperties) throws Exception {
        RedisTemplaterFactoryBuild redisTemplaterFactoryBuild = getRedisTemplaterFactoryBuild(dynamicRedisProperties);
        redisTemplaterFactoryBuild.setDefaultRedisTemplate(redisTemplate(redisConnectionFactory, redisTemplaterFactoryBuild,dynamicRedisProperties));
        return redisTemplaterFactoryBuild;
    }

    /**
     * RedisTemplaterWrapper为RedisTemplater的包装类
     * 当启用了多数据源配置时，RedisTemplaterWrapper中的数据源为配置中的其中一个
     *    1.当多数据源配置了默认数据源时，用默认数据源
     *    2.当多数据源没有配置默认数据源时，使用最后一个配置作为数据源
     * 当没有启用多数据源配置时，此类就是RedisTemplater的普通封装，配置什么数据源就用什么
     * @return
     * @throws Exception
     */
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory rcf, RedisTemplaterFactoryBuild fb, DynamicRedisProperties drp) throws Exception {
        RedisSerializer<Object> serializer = redisSerializer();
        RedisTemplate<String, Object> redisTemplate = null;
        if (fb.getRedisTemplateMap() != null) {
            //如果配置了默认的数据源，则使用默认的数据源
            String defaultDataSource = drp.getDefaultDataSource();
            if (!StringUtils.isEmpty(defaultDataSource)) {
                redisTemplate = fb.getRedisTemplaterByName(defaultDataSource);
            }else {
                //如果没有配置默认的数据源，则从所有配置的数据源中，选择最后一个配置作为数据源
                Map<String, RedisTemplate<String, Object>> allRedisTemplates = fb.getRedisTemplateMap();
                for (String key : allRedisTemplates.keySet()) {
                    redisTemplate = allRedisTemplates.get(key);
                    break;
                }
            }
        }else {
            redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(rcf);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(serializer);
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(serializer);
            redisTemplate.afterPropertiesSet();
        }
        return redisTemplate;
    }

    /**
     * 构造RedisTemplater工厂类
     * @param dynamicRedisProperties 动态数据源配置属性
     * @return
     */
    public RedisTemplaterFactoryBuild getRedisTemplaterFactoryBuild(DynamicRedisProperties dynamicRedisProperties) {
        RedisTemplaterFactoryBuild redisTemplaterFactoryBuild = new RedisTemplaterFactoryBuild();
        //如果开启多数据源配置，则获取多数据源对应构造出来的RedisTemplate
        if (dynamicRedisProperties.isEnabled()) {
            YamlRedisProvider yamlRedisProvider = new YamlRedisProvider(dynamicRedisProperties);
            Map<String, RedisTemplate<String, Object>> stringRedisTemplateMap = redisTemplatesMap(yamlRedisProvider);
            redisTemplaterFactoryBuild.setRedisTemplateMap(stringRedisTemplateMap);
        }
        return redisTemplaterFactoryBuild;
    }

    /**
     * 序列化
     * @return
     */
    @Bean
    public RedisSerializer<Object> redisSerializer() {
        //创建JSON序列化器
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //必须设置，否则无法将JSON转化为对象，会转化成Map类型
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }

    /**
     * 将多数据源配置built出多个RedisTemplate
     * @param redisProvider
     * @return
     */
    public Map<String, RedisTemplate<String, Object>> redisTemplatesMap(RedisProvider redisProvider) {
        Map<String, RedisConnectionFactory> stringRedisConnectionFactoryMap = redisProvider.loadRedis();
        Map<String,RedisTemplate<String, Object>> redisTemplatesMap = new HashMap<>();
        for (String key : stringRedisConnectionFactoryMap.keySet()) {
            RedisConnectionFactory redisDataSoure = stringRedisConnectionFactoryMap.get(key);
            RedisSerializer<Object> serializer = redisSerializer();
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisDataSoure);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(serializer);
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(serializer);
            redisTemplate.afterPropertiesSet();
            redisTemplatesMap.put(key, redisTemplate);
        }
        return redisTemplatesMap;
    }
}
